package com.lu.library.kv

import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.doublePreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.floatPreferencesKey
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStoreFile
import com.lu.library.CommLibInit
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map




class DataStoreManager {

    private var dataStore: DataStore<Preferences>? = null

    companion object {
        const val KEY_SP_NAME = "SP"
        private val dataStoreManagers = mutableMapOf<String, DataStoreManager>()

        val defaultDataStore = DataStoreManager().init(KEY_SP_NAME)
        suspend fun put(key: String, value: Any) {
            defaultDataStore.put(key, value)
        }

        suspend inline fun <reified T> get(key: String): T? {
            return defaultDataStore.get(key)
        }

        fun getInstance(name: String): DataStoreManager {
            var dataStore = dataStoreManagers.get(name)
            if (dataStore == null) {
                dataStore = DataStoreManager().init(name)
                dataStoreManagers[name] = dataStore
            }
            return dataStore
        }
    }

    fun init(name: String) = apply {
        dataStore = PreferenceDataStoreFactory.create {
            CommLibInit.getApp().preferencesDataStoreFile(name)
        }
    }

    fun requestDataStore(): DataStore<Preferences> {
        return dataStore ?: CommLibInit.dataStore
    }

    suspend fun put(key: String, value: Any) {
        when (value) {
            is String -> put(requestDataStore(), stringPreferencesKey(key), value)
            is Int -> put(requestDataStore(), intPreferencesKey(key), value)
            is Boolean -> put(requestDataStore(), booleanPreferencesKey(key), value)
            is Float -> put(requestDataStore(), floatPreferencesKey(key), value)
            is Double -> put(requestDataStore(), doublePreferencesKey(key), value)
            is Long -> put(requestDataStore(), longPreferencesKey(key), value)
        }
    }

    suspend inline fun <reified T> get(key: String): T? {
        return when (T::class) {
            String::class -> get(requestDataStore(), stringPreferencesKey(key)) as T?
            Int::class -> get(requestDataStore(), intPreferencesKey(key)) as T?
            Boolean::class -> get(requestDataStore(), booleanPreferencesKey(key)) as T?
            Float::class -> get(requestDataStore(), floatPreferencesKey(key)) as T?
            Double::class -> get(requestDataStore(), doublePreferencesKey(key)) as T?
            Long::class -> get(requestDataStore(), longPreferencesKey(key)) as T?
            else -> null
        }
    }

    suspend fun <T> put(dataStore: DataStore<Preferences>, key: Preferences.Key<T>, value: T) {
        dataStore.edit { preferences ->
            preferences[key] = value
        }
    }

    fun <T> getAsFlow(dataStore: DataStore<Preferences>, key: Preferences.Key<T>): Flow<T?> {
        val fv = dataStore.data.catch { e ->
            throw e
        }.filter { setting ->
            setting.contains(key)
        }.map {
            it[key]
        }

        return fv
    }

    suspend fun <T> get(dataStore: DataStore<Preferences>, key: Preferences.Key<T>): T? {
        var v: T? = null
        dataStore.edit { settings ->
            v = settings[key]
        }
        return v
    }
}